#ifndef __COMPLEX__
#define __COMPLEX__
//guard 防卫式声明 防止头文件被重复包含
#include <cmath>
//定义类模板，避免重复定义类，使用时根据类型实例化
//不带指针的类多半不用写析构函数
//template<typename T>
class complex
{
//private	只能被本类的函数访问
//protected	能被本类的函数和子类的函数访问
//public	可以被所有函数访问
public:
	//构造函数：名称要和类名一致. T r=0 为默认实参 没有返回类型
	complex (double r = 0, double i = 0)
		: re(r), im(i) // : re(x), im(i) 是初始化列表  使用列表初始化的效率更高,应尽量使用列表初始化
	{}
	//为了提高效率,使用引用传递参数,避免了参数的复制.若不希望在函数体内对输入参数进行修改,应使用const修饰输入参数
	//函数的参数应尽量使用引用传递.
	complex& operator += (const complex&);
	//为提高效率,若函数的返回值是原本就存在的对象,则应以引用形式返回.
	//若函数的返回值是临时变量,则只能通过值传递返回

	//inline 内联函数  类声明内定义的函数,自动成为inline函数
	//类声明外定义的函数需要加上inline关键字才能成为inline函数
	double real() const { return re; }
	// 若成员函数中不改变成员变量,应加以const修饰
	// const 对象只能调用const 成员函数, 普通对象可以调用const 成员函数也可以调用 普通成员函数
	void real(double r) { re = r; }
	// 函数重载 overloading 编译器编译后对函数重命名
	double imag() const { return im; }
	void image(double i) { im = i; }
private:
	double re, im; //定义复数的实部，虚部
	//单例模式 把构造函数放在private
	
	friend complex& __doapl (complex*, const complex&);
};
//类声明外定义的函数需要加上inline关键字才能成为inline函数
//inline只是编程者给编译器的一个建议,在编译时未必会真正被编译为inline函数.
//因此如果函数足够简单,我们就把它声明为inline就好了.
inline double imag(const complex& x)
{
	return x.imag();
}

inline double real(const complex& x)
{
	return x.real();
}

inline complex& __doapl(complex* ths, const complex& r)
{
	ths->re += r.re;
	ths->im += r.im;
	return *ths;
}
//同一类的各个对象互为友元,因此在类定义内可以访问其他对象的私有变量
//c++中操作符实际上就是一种函数
//操作符重载
//所有成员函数都含有一个隐式参数this，谁调用这个函数，this就指向它
inline complex& complex::operator += (const complex& r)
{
	return __doapl(this, r);
}
//编写类的时候注意事项
//1.构造函数中使用列表初始化(initialization list)为成员变量赋值.
//2.常量成员函数使用const修饰.
//3.参数的传递尽量考虑使用引用传递,若函数体内不改变传入的参数,应加以const修饰.
//4.返回值若非局部变量,其传递尽量考虑使用引用传递,
//5.数据放入private中,大部分函数放入public中.
//在类外声明或函数重载+
// 临时对象 typename();

inline complex operator + (const complex& x, const complex& y)
{
	return complex (real(x) + real(y), imag(x) + imag(y));
}

inline complex operator + (const complex& x, double y)
{
	return complex (real(x) + y, imag(x));
}

inline complex operator + (double x, const complex& y)
{
	return complex (x + real(y), imag(y));
}

#include <iostream>

inline std::ostream& operator << (std::ostream& os, const complex& x)
{
	return os << " re = " << real(x) << ", im = " << imag(x);
}
// 所谓stack 栈 所谓heap(堆)
// stack 是存在于某作用域scope的一块内存空间。例如当你调用函数，函数本身即
//会形成一个stack用来放置它所接受的参数，以及返回地址,以及local 变量.
// 在函数本体内声明的任何变量，其所使用的内存块都取自上述stack
// heap, 是指由操作系统提供的一块global内存空间，程序可动态分配从中获得若干块
// 使用new获得
// 1.stack object的生命周期: 其生命周期在作用域(大括号)结束之际结束
// 2.static object 的生命周期: 其生命周期在作用域(大括号)结束之后仍然存在,直到整个程序结束.
// 3.global object的生命周期:其生命在在整个程序结束之后才结束,也可以将其视为一种static object,其作用域是整个程序.
// 4.heap object的生命周期 其生命周期在它被deleted之际结束.若推出作用域时忘记delete指针p则会发生内存泄漏,
// 即p所指向的heap object 仍然存在,但指针p的生命周期却结束了,作用域之外再也无法操作p指向的heap object.
// new 的流程: 先分配memory，再调用ctor
// delete 操作 先调用析构函数，再释放内存
// array new 要搭配 array delete
//自然可以理解为什么new[]和delete[]应该配对使用了: delete操作符仅会调用一次析构函数,
//而delete[]操作符依次对每个元素调用析构函数.对于String这样带有指针的类,若将delete[]误用为delete会引起内存泄漏.
// 补充  static
// 对于类来说,non-static成员变量每个对象均存在一份,static成员变量、non-static和static成员函数在内存中仅存在一份.
// 其中non-static成员函数通过指定this指针获得函数的调用权,而non-static函数不需要this指针即可调用.
// 静态函数只能处理静态数据
// 
// class Account {
// 	public:
// 		static double m_rate;
// 		static void set_rate(const double& x) { m_rate = x; }
// };
// double Account::m_rate = 8.0;
// static成员变量需要在类声明体外进行初始化.
// int main() {
// 	Account::set_rate(5.0);

// 	Account a;
// 	a.set_rate(7.0);       // 调用static函数的方式有两种：1.通过object调用 2.通过class name调用
// }

// 单例模式 singleton
// class A {
// public:
// 	static A& getInstance();
// 	setup() {}
// private:
// 	A();
// 	A(const A& rhs);
// 	static A a;
// };

// A& A::getInstance()
// {
// 	static A a;
// 	return a;
// }

// cout 
// 模板 class template 
// template<typename T>
// class complex 
// {
// public:
// 	complex (T r=0, T i=0)
// 		:re(r), im(i)
// 	{}
// private:
// 	T re, im;
// }
// complex<double> c1(2.5, 1.5);  模板实例化
// complex<int> c2(2,6);
// function template 函数模板 T是关键字
// template <class T>
// inline const T& min(const T& a, const T& b)
// {
// 	return b < a ? b:a;
// }
// stone r1(2,3), r2(3,3), r3;
// r3 = min(r1, r2); 编译器会做实参推导
// namespace 
#endif